grammar plp4;

@header
{
	import java.lang.String;
	import java.util.HashMap;
}

@members
{
	TablaSimbolos tablaSimbolos = new TablaSimbolos();
	Stack<Integer> pilaEtiquetas = new Stack<Integer>();
	Stack<String> pilaBucles = new Stack<String>();
	Stack<Integer> pilaCatch = new Stack<Integer>();
	Traductor traductor = new Traductor();
	TablaTipos tablaTipos = new TablaTipos();
	SimboloFuncion funcionActual = null;
	final String initHeader = "/* plp.il */ \n\n.assembly extern mscorlib {}\n.assembly 'plp' {} \n\n\n";
	int nivel = 0;
	int numEtiq = 0;
	SimboloProperty propertyActual = null;
	boolean isCampo = false;
	boolean hayMain = false;
	boolean isMain = false;

	public void LanzarError(String error)
	{
		System.err.println(error);
		System.exit (1);
	}
}

@lexer::members
{
	public void emitErrorMessage(String m)
	{
		System.err.println( m );
		System.exit (1);		
	}
}

@rulecatch 
{ 
	catch (RecognitionException re)
	{ 
		reportError(re); 
		System.exit(1); 
	} 
} 


/* SINTAX READER */
s
@init {StringBuilder salida = new StringBuilder();} :
	(
		clase
		{
			salida.append($clase.trad);
		}

	)+
	{
		if ( !hayMain )
		{
			LanzarError("Error 22: debe existir un unico metodo Main");
		}
		System.out.println( initHeader + salida.toString());
	};

clase
returns [ String trad ]
@init {StringBuilder sb = new StringBuilder(); trad = ""; StringBuilder miembros = new StringBuilder();} :  
	CLASS ID
	{
		SimboloClase s = new SimboloClase($ID.text, new TipoClase(null, $ID.text));
		if ( !tablaSimbolos.addSimbolo(s) )
		{
			StringBuilder err = new StringBuilder();
			err.append("Error 1 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
			err.append("): '").append($ID.text).append("' ya existe en este ambito");
			LanzarError(err.toString());
		}
	}
	LLAVEI
	{
		tablaSimbolos = tablaSimbolos.push();
		nivel++;
	}
	(miembro
	{
		miembros.append($miembro.trad);
	})+
	LLAVED
	{
//		sb.append(tablaSimbolos.toString());
		sb.append(".class '").append($ID.text).append("' extends [mscorlib]System.Object\n{\n");
		if ( !tablaSimbolos.hasConstructor() )
		{
			sb.append(".method public specialname rtspecialname instance void .ctor (");
			sb.append(") cil managed");
			sb.append("\n{\n\t.maxstack 1\n\tldarg 0\n");
			sb.append("\tcall instance void [mscorlib]System.Object::.ctor()\n\tret\n}\n");
		}
		sb.append(miembros.toString());
		sb.append("\n}\n");
		tablaSimbolos = tablaSimbolos.pop();
		$trad = sb.toString();
	};

metodo
returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); $trad = "";} :  
		PUBLIC STATIC VOID MAIN PARI PARD
		{
			SimboloFuncion s = new SimboloFuncion("main", null);
			tablaSimbolos.addSimbolo(s);
			if ( hayMain )
			{
				LanzarError("Error 22: debe existir un unico metodo Main");
			}
			hayMain = true;
			isMain = true;
		}
		bloque[true,false]
		{
			isMain = false;
			salida.append(s.toString(tablaSimbolos.getLocalsValue()));
			salida.append($bloque.trad).append("\tret\n}\n");
			$trad = salida.toString();
			tablaSimbolos = tablaSimbolos.pop();
		}
	|
		visibilidad (tipoSimple)? ID PARI args PARD
		{
			if ( $tipoSimple.tipo == null && !$ID.text.equals(tablaSimbolos.getCurrentClass().getNombre()) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 36 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1).append("): un metodo debe devolver algo");
				LanzarError( sb.toString() );
			}
			if ( $tipoSimple.tipo != null && $ID.text.equals(tablaSimbolos.getCurrentClass().getNombre()) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 35 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1).append("): un constructor no puede devolver nada");
				LanzarError( sb.toString() );
			}
			funcionActual = new SimboloFuncion($ID.text, $tipoSimple.tipo, $args.argumentos);
			SimboloFuncion s = new SimboloFuncion($ID.text, $tipoSimple.tipo, $args.argumentos);
			if ( $tipoSimple.tipo == null)
			{
				if ( $args.argumentos.size() == 0)
					tablaSimbolos.setConstructor();
				s.setConstructor();
			}
			s.setClasePadre(tablaSimbolos.getCurrentClass().getNombre());
			s.setVisibilidad($visibilidad.visibilidad);
			if ( !tablaSimbolos.addSimbolo(s) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 24 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1).append("): el metodo '");
				sb.append(tablaSimbolos.getCurrentClass().getNombre()).append(".").append($ID.text).append("(");
				sb.append($args.tError).append(")' ya esta definido");
				LanzarError( sb.toString() );
			}
		}
		b2=bloque[true,true]
		{
			if ( $tipoSimple.tipo == null)
			{
				salida.append(".method ").append($visibilidad.trad).append(" specialname rtspecialname instance void .ctor (");
				salida.append($args.trad);
				salida.append(") cil managed");
				salida.append("\n{\n\t.maxstack 1000\n");
				salida.append("\t.locals(").append($bloque.locals).append(")\n\tldarg 0\n");
				salida.append("\tcall instance void [mscorlib]System.Object::.ctor()");
				salida.append($b2.trad);
				salida.append("\n\tret\n}\n");
			}
			else
			{
				salida.append(".method ").append($visibilidad.trad).append(" ");
				salida.append($tipoSimple.tipo.toWrite()).append(" ");
				salida.append("'").append($ID.text).append("' (");
				salida.append($args.trad);
				salida.append(") cil managed");
				salida.append("\n{\n\t.maxstack 1000\n");
				salida.append("\t.locals(").append($bloque.locals).append(")\n");
				salida.append($b2.trad);
				salida.append("\n\tldc.r8 0\n\tret\n");
				salida.append("\n}\n");
			}
			$trad = salida.toString();
			funcionActual = null;
		};

tipoSimple returns [ Tipo tipo, Integer linea, Integer col ]
@init { $linea = -1; $col = -1;}: 
		INT 
		{
			$tipo = new TipoEntero();
			$linea = $INT.getLine();
			$col = $INT.getCharPositionInLine()+1;
		}
	| 
		DOUBLE
		{
			$tipo = new TipoReal();
			$linea = $DOUBLE.getLine();
			$col = $DOUBLE.getCharPositionInLine()+1;
		}
	|
		BOOL
		{
			$tipo = new TipoBooleano();
			$linea = $BOOL.getLine();
			$col = $BOOL.getCharPositionInLine()+1;
		}
	;

tipo returns[Tipo tipo, Integer linea, Integer col]:
		ID
		{
			SimboloClase s = tablaSimbolos.getClass($ID.text);
			if ( s == null )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 2 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1).append("): simbolo '");
				sb.append($ID.text).append("' no ha sido declarado");
				LanzarError( sb.toString() );
			}
			$tipo = s.getTipo();
			$linea = $ID.getLine();
			$col = $ID.getCharPositionInLine()+1;
		}
	|
		tipoSimple
		{
			$tipo = $tipoSimple.tipo;
			$linea = $tipoSimple.linea;
			$col = $tipoSimple.col;
		};

decl[boolean visibilidad] returns [ String trad, Simbolo dec, String fTrad ]
@init {$trad = "";} :
		constdecl 
		{
			$trad = $constdecl.trad;
			$dec = $constdecl.dec;
			$fTrad = $constdecl.fTrad;
		}
	|
		vardecl[visibilidad]
		{
			$fTrad = $vardecl.fTrad;
			$trad = $vardecl.trad;
			$dec = $vardecl.dec;
		}
	;

constdecl returns [ String trad, Simbolo dec, String fTrad]
@init {$trad = ""; StringBuilder salida = new StringBuilder(); $fTrad = "";} :
	CONST tipoSimple c1=constid[$tipoSimple.tipo]
	{
		salida.append($c1.trad);
		$dec = $c1.dec;
	}
	(
		COMA c2=constid[$tipoSimple.tipo]
		{
			salida.append($c2.trad);
		}
	)*
	PYC
	{
		$trad = salida.toString();
		$fTrad = $trad;
	};

constid[Tipo tipo] returns [ String trad, Simbolo dec ]
@init {$trad = ""; StringBuilder salida = new StringBuilder();} :
	ID ASIG
	(
			ENTERO 
			{
				Simbolo s;
				if ( !isCampo )
					s = new SimboloEntero($ID.text, tipo );
				else
				{
					s = new SimboloCampo($ID.text, tipo );
					((SimboloCampo)s).setClasePadre(tablaSimbolos.getCurrentClass().getNombre());
				}
				s.setConstante();
				if ( !tablaSimbolos.addSimbolo(s) )
				{
					StringBuilder err = new StringBuilder();
					err.append("Error 1 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
					err.append("): '").append($ID.text).append("' ya existe en este ambito");
					LanzarError(err.toString());
				}
				if ( !tipo.isEntero() )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 6 (").append($ASIG.getLine()).append(",").append($ASIG.getCharPositionInLine()+1);
					sb.append(": tipos incompatibles en la instruccion de asignacion");
					LanzarError( sb.toString() );
				}
				if ( !isCampo )
				{
					salida.append("\tldc.i4 ").append($ENTERO.text).append("\n");
					salida.append("\tstloc ").append(s.getLocalPos()).append("\n");
				}
				else
				{
					salida.append(".data data_").append(tablaSimbolos.getCurrentClass().getNombre()).append("_").append($ID.text).append(" = int32(").append($ENTERO.text).append(")\n");
					salida.append(".field public static int32 '").append($ID.text).append("' at data_").append(tablaSimbolos.getCurrentClass().getNombre()).append("_").append($ID.text).append("\n");
				}
				$trad = salida.toString();
				$dec = s;
			}
		|
			REAL
			{
				Simbolo s;
				if ( !isCampo )
					s = new SimboloReal($ID.text, tipo );
				else
					s = new SimboloCampo($ID.text, tipo );
				s.setConstante();
				s.setConstante();
				if ( !tablaSimbolos.addSimbolo(s) )
				{
					StringBuilder err = new StringBuilder();
					err.append("Error 1 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
					err.append("): '").append($ID.text).append("' ya existe en este ambito");
					LanzarError(err.toString());
				}
				if ( !tipo.isReal() )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 6 (").append($ASIG.getLine()).append(",").append($ASIG.getCharPositionInLine()+1);
					sb.append(": tipos incompatibles en la instruccion de asignacion");
					LanzarError( sb.toString() );
				}
				salida.append("\tldc.r8 ").append($REAL.text).append("\n");
				salida.append("\tstloc ").append(s.getLocalPos()).append("\n");
				$trad = salida.toString();
				$dec = s;
			}
		|
			BOOLEANO
			{
				Simbolo s;
				if ( !isCampo )
					s = new SimboloBooleano($ID.text, tipo );
				else
					s = new SimboloCampo($ID.text, tipo );
				s.setConstante();
				if ( !tablaSimbolos.addSimbolo(s) )
				{
					StringBuilder err = new StringBuilder();
					err.append("Error 1 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
					err.append("): '").append($ID.text).append("' ya existe en este ambito");
					LanzarError(err.toString());
				}
				if ( !tipo.isBoolean() )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 6 (").append($ASIG.getLine()).append(",").append($ASIG.getCharPositionInLine()+1);
					sb.append(": tipos incompatibles en la instruccion de asignacion");
					LanzarError( sb.toString() );
				}
				salida.append("ldc.i4 ");
				if ( $BOOLEANO.text.equals("True") )
				{
					salida.append("1\n");
				}
				else
				{
					salida.append("0\n");
				}
				salida.append("stloc ").append(s.getLocalPos()).append("\n");
				$trad = salida.toString();
				$dec = s;
			}
	);

vardecl[boolean visibilidad] returns [ String trad, Simbolo dec, String fTrad ]
@init {StringBuilder salida = new StringBuilder(); $trad = ""; $fTrad = "";} :
	tipo v1=varid[$tipo.tipo, visibilidad]
	{
		$dec = $v1.dec;
		salida.append($dec.toString());
	}
	(
		COMA v2=varid[$tipo.tipo, visibilidad]
		{
			salida.append($v2.dec.toString());
		}
	)*
	PYC
	{
		$fTrad = salida.toString();
	};

varid[Tipo tipo, boolean visibilidad] returns [Simbolo dec]
@init {Tipo tipoFinal;Simbolo s = null;} :
	ID
	{
		if ( !isCampo )
		{
			if ( tipo.isEntero() )
			{
				s = new SimboloEntero($ID.text, tipo);
			}
			else if ( tipo.isReal() )
			{
				s = new SimboloReal($ID.text, tipo);
			}
			else if ( tipo.isBoolean() )
			{
				s = new SimboloBooleano($ID.text, tipo);
			}
			else if ( tipo.isClass() )
			{
				s = new SimboloObjeto($ID.text, tipo);
			}
		}	
		else
		{
			s = new SimboloCampo($ID.text, tipo);
			((SimboloCampo)s).setClasePadre(tablaSimbolos.getCurrentClass().getNombre());
			s.setVisibilidad(visibilidad);
		}
		$dec = s;
	}
	(
		CORI
		{
			if ( s.isObjeto() )
			{
				StringBuilder err = new StringBuilder();
				err.append("Error 29 (").append($CORI.getLine()).append(",").append($CORI.getCharPositionInLine()+1);
				err.append("): no se permite la declaracion de arrays de objetos");
				LanzarError(err.toString());
			}
			tipoFinal = new TipoArray(tipo, null);
			s.setTipo(tipoFinal);
		}
		(
			{
				TipoArray t = new TipoArray(tipoFinal, null);
				tipoFinal = t;
				s.setTipo(tipoFinal);
			}
			COMA
		)*
		CORD
	)?
	{
		if ( !tablaSimbolos.addSimbolo(s) )
		{
			StringBuilder err = new StringBuilder();
			err.append("Error 1 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
			err.append("): '").append($ID.text).append("' ya existe en este ambito");
			LanzarError(err.toString());
		}
	};

declins returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :
	(
		instr[true]
		{
			salida.append($instr.trad);
		}
		|
		decl[true]
		{
			salida.append($decl.trad);
		}
	)*
	{
		$trad = salida.toString();
	};

bloque[boolean pop, boolean isMetodo]
returns [ String trad, String locals ]
@init {StringBuilder salida = new StringBuilder(); $trad = ""; $locals = "";} :
	LLAVEI
	{
		if ( isMetodo )
		{
			tablaSimbolos = tablaSimbolos.pushMetodo();
		}
		else if ( pop )
		{
			tablaSimbolos = tablaSimbolos.push();
		}
	}
	declins
	{
		$trad = $declins.trad;
	}
	LLAVED
	{
		if ( isMetodo )
		{
			$locals = tablaSimbolos.getLocalsValue();
			tablaSimbolos = tablaSimbolos.popMetodo();
		}
		else if ( pop )
		{
			tablaSimbolos = tablaSimbolos.pop();
		}
	};

instr[boolean pop] returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :
		bloque[pop, false]
		{
			$trad = $bloque.trad;
		}
	| 
		IF PARI e3=expr[null]
		{
			if ( $e3.sr != null && !$e3.sr.isBooleano() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 5 (").append($IF.getLine()).append(",").append($IF.getCharPositionInLine()+1).append("):");
				sb.append(" la expresion debe ser de tipo booleano en la instruccion '");
				sb.append($IF.text).append("'");
				LanzarError( sb.toString() );
			}
			salida.append($e3.trad);
			salida.append("\tldc.i4.0\n").append("\tbeq L").append(numEtiq).append(" //SALTO SI FALSO\n"); //Si falso saltar
			pilaEtiquetas.push(numEtiq);
			numEtiq++;
		}
		PARD i1=instr[true]
		{
			int etiq = pilaEtiquetas.pop();
			salida.append($i1.trad);
			salida.append("\tbr L").append(etiq).append("FIN //FIN IF NO ESTRAR ELSE\n");
			salida.append("\tL").append(etiq).append(": //ELSE\n");
		}
		(
			ELSE i2=instr[true]
			{
				salida.append($i2.trad);
			}
		)?
		{
			salida.append("\tL").append(etiq).append("FIN: //FIN ELSE\n");
			numEtiq++;
			$trad = salida.toString();
		}
	|
		WHILE PARI e4=expr[null]
		{
			if (  $e4.sr != null && !$e4.sr.isBooleano() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 5 (").append($WHILE.getLine()).append(",").append($WHILE.getCharPositionInLine()+1).append("):");
				sb.append(" la expresion debe ser de tipo booleano en la instruccion '");
				sb.append($WHILE.text).append("'");
				LanzarError( sb.toString() );
			}
			int etiq = numEtiq;
			salida.append("\tW").append(numEtiq).append(": //BUCLE WHILE \n");
			pilaBucles.push("W" + numEtiq);
			numEtiq++;
			salida.append($e4.trad);
			salida.append("\tldc.i4.0\n").append("\tbeq W").append(etiq).append("FIN //COMPROBAR CONDICION WHILE\n"); //Si falso saltar
		}
		PARD i3=instr[true]
		{
			salida.append($i3.trad);
			salida.append("\tbr W").append(etiq).append(" //SIGUIENTE ITERACION DE WHILE\n");
			salida.append("\tW").append(etiq).append("FIN: //FIN WHILE\n");
			$trad = salida.toString();
			pilaBucles.pop();
		}
	|
		FOR PARI INT ID 
		{
			tablaSimbolos = tablaSimbolos.push();
			Simbolo s = new SimboloEntero($ID.text);
			s.setConstante();
			if ( !tablaSimbolos.addSimbolo(s) )
			{
				StringBuilder err = new StringBuilder();
				err.append("Error 1 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
				err.append("): '").append($ID.text).append("' ya existe en este ambito");
				LanzarError(err.toString());
			}
		}
		ASIG e1=expr[s] TO e2=expr[null] STEP (ADDOP)? ENTERO PARD
		{
			if ( $e2.trad.lastIndexOf("clt") != -1 || $e2.trad.lastIndexOf("cgt") != -1 || $e2.trad.lastIndexOf("ceq") != -1   )
			{
				StringBuilder err = new StringBuilder();
				err.append("Error 17 (").append($TO.getLine()).append(",").append($TO.getCharPositionInLine()+1);
				err.append("): la expresion debe ser de tipo numerico");
				LanzarError(err.toString());
			}
			pilaBucles.push("F" + numEtiq);
			numEtiq++;
		}
		i4=instr[false]
		{
			salida.append($e1.trad);
			salida.append("\tconv.i\n");
			salida.append("\tstloc ").append(s.getLocalPos()).append(" //ASIGNACION DEL FOR\n");
			
			String etiq = pilaBucles.pop();
			salida.append(etiq).append(": //INICIO BUCLE FOR \n");
			salida.append($e2.trad);
			salida.append("\tldloc ").append(s.getLocalPos()).append("\n");
			if ( $e2.sr.isReal() )
			{
				salida.append("\tconv.r8 \n");
			}
			if ( $ADDOP != null && $ADDOP.text.equals("-"))
			{
				salida.append("\tcgt\n\tldc.i4.1\n\txor //MIETRAS ITERADOR > CONDICION \n");
			}
			else
			{
				salida.append("\tclt\n\tldc.i4.1\n\txor //MIENTRAS ITERADOR < CONDICION \n");
			}
			salida.append("\tldc.i4.0\n").append("\tbeq ").append(etiq).append("FIN //COMPROBAR CONDICION FOR\n"); //Si falso saltar
			salida.append($i4.trad);
			salida.append(etiq).append("INC: //INCREMETO DEL ITERADOR\n");
			salida.append("\tldloc ").append(s.getLocalPos()).append("\n");
			salida.append("\tldc.i4 ").append($ENTERO.text).append("\n");
			if ( $ADDOP != null && $ADDOP.text.equals("-"))
			{
				salida.append("\tsub\n");
			}
			else
			{
				salida.append("\tadd\n");
			}
			salida.append("\tstloc ").append(s.getLocalPos()).append("\n");
			salida.append("\tbr ").append(etiq).append("\n");
			salida.append(etiq).append("FIN: //FIN BUCLE FOR\n");
			$trad = salida.toString();
			tablaSimbolos = tablaSimbolos.pop();
		}
	|
		BREAK PYC
		{
			if ( pilaBucles.empty() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 16 (").append($BREAK.getLine()).append(",").append($BREAK.getCharPositionInLine()+1);
				sb.append("): instruccion '");
				sb.append($BREAK.text).append("' no permitida fuera de un bucle");
				LanzarError( sb.toString() );
			}
			else
			{
				salida.append("\tbr ").append(pilaBucles.peek()).append("FIN\n");
				$trad = salida.toString();
			}
		}
	|
		CONTINUE PYC
		{
			if ( pilaBucles.empty() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 16 (").append($CONTINUE.getLine()).append(",").append($CONTINUE.getCharPositionInLine()+1);
				sb.append("): instruccion '");
				sb.append($CONTINUE.text).append("' no permitida fuera de un bucle");
				LanzarError( sb.toString() );
			}
			else
			{
				String salto = pilaBucles.peek();
				if ( salto.charAt(0) == 'F' )
				{
					salida.append("\tbr ").append(pilaBucles.peek()).append("INC\n");
				}
				else
				{
					salida.append("\tbr ").append(pilaBucles.peek()).append("\n");
				}
				$trad = salida.toString();
			}
		}
	|
		ref["cambio"] cambio[$ref.s, $ref.trad, $ref.sInicio, $ref.cont]
		{
			if ( $ref.s.isProperty()  )
			{
				if ( funcionActual == null )
					salida.append("\n\tldloc ").append($ref.sInicio.getLocalPos()).append("\n");
				else
					salida.append("\n\tldarg 0\n");
			}
			salida.append($cambio.trad);
			$trad = salida.toString();
		}
	|
		ID
		{
			Simbolo s = tablaSimbolos.getSimbolo($ID.text);
			if ( s == null )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 2 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1).append("): simbolo '");
				sb.append($ID.text).append("' no ha sido declarado");
				LanzarError( sb.toString() );
			}
			if ( s.getTipo() == null || !s.getTipo().isArray() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 11 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1).append("): el identificador '");
				sb.append($ID.text);
				sb.append("' no es de tipo array");
				LanzarError( sb.toString() );
			}
		}
		ASIG NEW tipoSimple
		{
			Tipo t = $tipoSimple.tipo;
			if ( !t.getNombre().equals(s.getTipo().getTipoSimple().getNombre()) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 14 (").append($tipoSimple.linea).append(",").append($tipoSimple.col);
				sb.append("): tipo '");
				sb.append(t.getNombre());
				sb.append("' incompatible con la declaracion");
				LanzarError( sb.toString() );
			}
			TipoArray tArray = (TipoArray)s.getTipo();
		}
		CORI dims[tArray] CORD
		{
			if ( tArray.getDimensiones() > $dims.dimensiones)
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 10 (").append($CORD.getLine()).append(",").append($CORD.getCharPositionInLine()+1);
				sb.append("): numero de dimensiones incorrecto");
				LanzarError( sb.toString() );
			}
			tArray.setInicializado();
			if ( s.isCampo() )
				salida.append("\tldarg 0\n");
			salida.append("\tldc.i4 ").append(tArray.getTotalSize()).append(" //TAMAÑO DEL VECTOR ").append(s.getNombre()).append(" \n");
			salida.append("\tnewarr [mscorlib]System.").append(tArray.getTipoSimpleString()).append(" //RESERVA DE MEMORIA DEL VECTOR\n");
			if ( s.isCampo() )
			{
				salida.append("\n\tstfld ").append(s.toWrite()).append(" '").append(((SimboloCampo)s).getClasePadre()).append("'::'");
				salida.append(s.getNombre()).append("'\n");
			}
			else
				salida.append("\tstloc ").append(s.getLocalPos()).append("\n");
			$trad = salida.toString();
		}
		PYC
	|
		WRITELINE PARI ex1=expr[null]
		{
			salida.append($ex1.trad);
			salida.append("\tcall void [mscorlib]System.Console::WriteLine(");
			salida.append($ex1.cima).append(")\n");
			$trad = salida.toString();
			if ( $ex1.sr.isObjeto() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 32 (").append($WRITELINE.getLine()).append(",").append($WRITELINE.getCharPositionInLine()+1);
				sb.append("): la referencia es de tipo objeto");
				LanzarError( sb.toString() );
			}
		}
		PARD PYC
	|
		TRY
		{
			pilaEtiquetas.push(numEtiq);
			numEtiq++;
		if ( !pilaCatch.empty() )
		{
			StringBuilder sb = new StringBuilder();
			sb.append("Error 18 (").append($TRY.getLine()).append(",").append($TRY.getCharPositionInLine()+1);
			sb.append("): sentencia '");
			sb.append($TRY.text);
			sb.append("' no permitida en este ambito");
			LanzarError( sb.toString() );
		}
		}
		b2=bloque[true, false] bloquecatch[pilaEtiquetas.peek()]
		{
			salida.append("\t.try\n{\n");
			salida.append($b2.trad);
			salida.append("\tleave L").append(pilaEtiquetas.pop()).append("\n}\n");
			salida.append($bloquecatch.trad);
			$trad = salida.toString();
		}
	|
		THROW ( NEW EXCEPTION PARI ex2=expr[null] PARD )? PYC
		{
			if ( $NEW == null )
			{
				$trad = "\trethrow\n";
			}
			else
			{
				salida.append($ex2.trad);
				salida.append("\tcall string [mscorlib]System.Convert::ToString(").append($ex2.cima).append(")\n");
				salida.append("\tnewobj instance void [mscorlib]System.Exception::.ctor(string)\n");
				salida.append("\tthrow\n");
				$trad = salida.toString();
			}
		}
	| 
		EXPLAIN ID PYC
		{
			if ( pilaCatch.empty() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 18 (").append($EXPLAIN.getLine()).append(",").append($EXPLAIN.getCharPositionInLine()+1);
				sb.append("): sentencia '");
				sb.append($EXPLAIN.text);
				sb.append("' no permitida en este ambito");
				LanzarError( sb.toString() );
			}
			if ( !tablaSimbolos.getSimbolo($ID.text).isExcepcion() ) 
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 19 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
				sb.append("): '");
				sb.append($ID.text);
				sb.append("' no es una excepcion");
				LanzarError( sb.toString() );
			}
			salida.append("\tdup\n");
			salida.append("\tcall instance string [mscorlib]System.Exception::get_Message()\n");
			salida.append("\tcall void [mscorlib]System.Console::WriteLine(string)\n");
			$trad = salida.toString();
		}
	|
		RETURN
		{
			if ( funcionActual == null && propertyActual == null  )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 26 (").append($RETURN.getLine()).append(",").append($RETURN.getCharPositionInLine()+1).append("): aqui no puede usarse return");
				LanzarError( sb.toString() );
			}
		}
		exa=expr[null]
		PYC
		{
			if ( funcionActual != null )
			{
				funcionActual.setHasReturn(true);
				if ( !funcionActual.compatible($expr.sr) )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 27 (").append($RETURN.getLine()).append(",").append($RETURN.getCharPositionInLine()+1).append("): valor devuelto de tipo incompatible");
					LanzarError( sb.toString() );
				}
			}
			salida.append($exa.trad);
			salida.append("\tret\n");
			$trad = salida.toString();
		}
	|
		id1=ID ASIG NEW id2=ID PARI params[$PARI] PARD PYC
		{
			Simbolo sc = tablaSimbolos.getSimbolo($id1.text) ;
			if ( sc == null )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 2 (").append($id1.getLine()).append(",").append($id1.getCharPositionInLine()+1).append("): simbolo '");
				sb.append($id1.text).append("' no ha sido declarado");
				LanzarError( sb.toString() );
			}
			if ( isMain && sc.isCampo() || sc.isProperty() || sc.isFuncion() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 31 (").append($id1.getLine()).append(",").append($id1.getCharPositionInLine()+1).append("): miembro '");
				sb.append($id1.text).append("' no accesible desde Main");
				LanzarError( sb.toString() );
			}
			if ( !sc.getTipo().getNombre().equals($id2.text) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 30 (").append($id1.getLine()).append(",").append($id1.getCharPositionInLine()+1).append("): objeto '");
				sb.append($id1.text).append("' no es de clase '").append($id2.text).append("'");
				LanzarError( sb.toString() );
			}

			if ( sc.isObjeto() )
				((SimboloObjeto)sc).init();
			else
				((SimboloCampo)sc).init();
			salida.append($params.trad);
			salida.append("\tnewobj instance void '").append($id2.text).append("'::.ctor(").append($params.tParams).append(")\n");
			salida.append("\tstloc ").append(sc.getLocalPos()).append("\n");
			$trad = salida.toString();
		}
	|
		subref[0] PARI params[$PARI] PARD PYC
		{
			salida.append($subref.trad);
			Simbolo sp = $subref.sInicio;
			String name = $subref.text + "[" + $params.size + "]";
			if ( funcionActual == null )
				salida.append("\tldloc ").append($subref.sInicio.getLocalPos()).append("\n");
			else
				salida.append("\n\tldarg 0");
			TablaSimbolos ts = null;
			if ( sp.getTipo() == null || sp.getTipo().getNombre().equals(tablaSimbolos.getCurrentClass().getNombre()) )
			{
				ts = tablaSimbolos;
			}
			else
			{
				ts = ((TipoClase)sp.getTipo()).getTablaSimbolos();
			}
			SimboloFuncion sf = (SimboloFuncion)ts.getSimbolo(name);
			if ( sf.isConstructor() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 34 (").append($subref.id.getLine()).append(",").append($subref.id.getCharPositionInLine()+1);
				sb.append("): constructor usado incorrectamente");
				LanzarError( sb.toString() );
			}
			salida.append($params.trad);
			salida.append("\tcall instance ").append(sf.toWrite()).append(" '").append(sf.getClasePadre()).append("'::'").append(sf.getNombre()).append("'(").append(sf.getArgsAsString()).append(")\n");
			if ( sf.getTipo() != null )
				salida.append("\tpop\n");
			$trad = salida.toString();
		};

bloquecatch[int etiqueta] returns [String trad]
@init {StringBuilder salida = new StringBuilder();}:
	CATCH PARI EXCEPTION ID PARD
	{
		SimboloExcepcion s = new SimboloExcepcion($ID.text);
		tablaSimbolos.addSimbolo(s);
		pilaCatch.push(1);
	}
	bloque[true,false]
	{
		salida.append("\tcatch [mscorlib]System.Exception\n{\n");
		salida.append($bloque.trad);
		salida.append("\tleave L").append(etiqueta).append("\n").append("}\n");
		salida.append("\tL").append(etiqueta).append(":\n");
		$trad = salida.toString();
		pilaCatch.pop();
	};

dims[TipoArray t] returns [int dimensiones]
@init {dimensiones = 0;} :
	e1=ENTERO
	{
		TipoArray t2 = (TipoArray)t;
		t2.setSize($e1.text);
		if ( t2.getSize() < 1 )
		{
			StringBuilder sb = new StringBuilder();
			sb.append("Error 8 (").append($e1.getLine()).append(",").append($e1.getCharPositionInLine()+1);
			sb.append("): tamanyo incorrecto");
			LanzarError( sb.toString() );
		}
		dimensiones++;
	}
	(
		COMA e2=ENTERO
		{
			if ( t2.getTipoBase().isArray() )
			{
				t2 = (TipoArray)t2.getTipoBase();
			}
			dimensiones++;
			if ( t.getDimensiones() < dimensiones)
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 10 (").append($COMA.getLine()).append(",").append($COMA.getCharPositionInLine()+1);
				sb.append("): numero de dimensiones incorrecto");
				LanzarError( sb.toString() );
			}
			t2.setSize($e2.text);
			if ( t2.getSize() < 1 )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 8 (").append($e2.getLine()).append(",").append($e2.getCharPositionInLine()+1);
				sb.append("): tamanyo incorrecto");
				LanzarError( sb.toString() );
			}
		}
	)*;

cambio[Simbolo s, String ref, Simbolo sInicio, int cont] returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); $trad = "";} :
		ASIG expr[s] PYC
		{
			if ( s.isConstante() || ( s.isProperty() && !((SimboloProperty)s).hasSet() ) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 15 (").append($ASIG.getLine()).append(",").append($ASIG.getCharPositionInLine()+1);
				sb.append("): la variable que se intenta modificar es de solo lectura");
				LanzarError( sb.toString() );
			}
			if ( propertyActual != null && $expr.sr.getNombre().equals("valueAux") )
			{
				//if ( $expr.sr.getNombre().equals("valueAux"))
				//{
					salida.append("\tldarg 1\n");
					salida.append("\tst").append(ref);
				//}
			}
			else if ( !s.getTipo().isArray() )
			{
				if ( s.isObjeto() && !$expr.sr.getTipo().getNombre().equals(s.getTipo().getNombre() ) )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 6 (").append($ASIG.getLine()).append(",").append($ASIG.getCharPositionInLine()+1);
					sb.append("): tipos incompatibles en la instruccion de asignacion");
					LanzarError( sb.toString() );
				}
				if( ( s.isBooleano() && !$expr.sr.isBooleano() ) || ( !s.isBooleano() && $expr.sr.isBooleano() ) )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 6 (").append($ASIG.getLine()).append(",").append($ASIG.getCharPositionInLine()+1);
					sb.append("): tipos incompatibles en la instruccion de asignacion");
					LanzarError( sb.toString() );
				}
				if ( s.isCampo() && ( funcionActual != null || propertyActual != null ) )
					salida.append("\tldarg 0\n");
				salida.append($expr.trad);
				if ( s.isEntero() && $expr.sr.isReal() )
				{
					salida.append("\tconv.i\n");
				}
				else if ( s.isReal() && $expr.sr.isEntero() )
				{
					salida.append("\tconv.r8\n");
				}
				if ( !s.isProperty() )
					salida.append("\tst");
				salida.append(ref);
			}
			else
			{
				salida.append(ref);
				salida.append($expr.trad);
				if ( s.isEntero() && $expr.sr.isReal() )
				{
					salida.append("\tconv.i\n");
				}
				else if ( s.isReal() && $expr.sr.isEntero() )
				{
					salida.append("\tconv.r8\n");
				}
				if ( s.isReal() )
				{
					salida.append("\tstelem.r8\n");
				}
				else
				{
					salida.append("\tstelem.i4\n");
				}
			}
			$trad = salida.toString();
		}
	|
		PUNTO READLINE PYC
		{
			if ( s.isConstante() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 15 (").append($READLINE.getLine()).append(",").append($READLINE.getCharPositionInLine()+1);
				sb.append("): la variable que se intenta modificar es de solo lectura");
				LanzarError( sb.toString() );
			}
			if ( s.isObjeto() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 32 (").append($READLINE.getLine()).append(",").append($READLINE.getCharPositionInLine()+1);
				sb.append("): la referencia es de tipo objeto");
				LanzarError( sb.toString() );
			}
			if ( s.getTipo().isArray() )
			{
				salida.append(ref);
			}
			if ( funcionActual != null && s.isCampo() && !s.getTipo().isArray() )
				salida.append("\tldarg 0\n");
			salida.append("\tcall string [mscorlib]System.Console::ReadLine()\n");
			if ( s.isEntero() || s.getTipo().getTipoSimple().isEntero() )
			{
				if ( $READLINE.text.charAt(0) != 'i' )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 7 (").append($READLINE.getLine()).append(",").append($READLINE.getCharPositionInLine()+1);
					sb.append("): tipos incompatibles en la instruccion de lectura");
					LanzarError( sb.toString() );
				}
				salida.append("\tcall int32[mscorlib]System.Int32::Parse(string)\n");
			}
			else if ( s.isBooleano() )
			{
				if ( $READLINE.text.charAt(0) != 'b' )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 7 (").append($READLINE.getLine()).append(",").append($READLINE.getCharPositionInLine()+1);
					sb.append("): tipos incompatibles en la instruccion de lectura");
					LanzarError( sb.toString() );
				}
				salida.append("\tcall bool[mscorlib]System.Boolean::Parse(string)\n");
			}
			else
			{
				if ( $READLINE.text.charAt(0) != 'd' )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 7 (").append($READLINE.getLine()).append(",").append($READLINE.getCharPositionInLine()+1);
					sb.append("): tipos incompatibles en la instruccion de lectura");
					LanzarError( sb.toString() );
				}
				salida.append("\tcall float64[mscorlib]System.Double::Parse(string)\n");
			}
			if ( !s.getTipo().isArray() )
			{
				if ( s.isCampo() )
				{
					SimboloCampo sc = (SimboloCampo)s;
					salida.append("\tstfld ").append(sc.toWrite()).append(" '").append(sc.getClasePadre()).append("'::'").append(sc.getNombre()).append("'\n");
				}
				else
					salida.append("\tstloc ").append(s.getLocalPos()).append("\n");
			}
			else
			{
				if ( s.isReal() )
				{
					salida.append("\tstelem.r8\n");
				}
				else
				{
					salida.append("\tstelem.i4\n");
				}
			}
			$trad = salida.toString();
		};

expr[Simbolo sh] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = "";} :
	e1=eand[sh]
	{
		$sr = $e1.sr;
		$cima = $e1.cima;
		salida.append($e1.trad);
	}
	(
		OR e2=eand[sh]
		{
			if (  $e1.sr != null && $e2.sr != null && ( !$e2.sr.isBooleano() || !$e1.sr.isBooleano() ) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 4 (").append($OR.getLine()).append(",").append($OR.getCharPositionInLine()+1).append("):");
				sb.append(" tipo incompatible en operador logico '");
				sb.append($OR.text).append("'");
				LanzarError( sb.toString() );
			}
			$sr = new SimboloBooleano("");
			$cima = "bool";
			salida.append($e2.trad);
			salida.append("\tor\n");
		}
	)*
	{
		if ( $cima.equals("") )
		{
			$cima = $e1.cima;
		}
		salida.append("\n");
		$trad = salida.toString();
	};

eand[Simbolo sh] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = "";} :
	e1=erel[sh]
	{
		salida.append($e1.trad);
		$cima = $e1.cima;
		$sr = $e1.sr;
	}
	(
		AND e2=erel[sh]
		{
			$cima = "bool";
			$sr = new SimboloBooleano("");
			salida.append($e2.trad);
			salida.append("\tand\n");
		}
	)*
	{
		if ( $cima.equals("") )
		{
			$cima = $e1.cima;
		}
		$trad = salida.toString();
	};

erel[Simbolo sh] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = ""; Simbolo sant = null;} :
	e1=esum[sh]
	{
		salida.append($e1.trad);
		$sr = $e1.sr;
		$cima = $e1.cima;
		sant = $e1.sr;
	}
	(
		RELOP e2=esum[$e1.sr]
		{
			$cima = "bool";
			$sr = new SimboloBooleano("");
			if ( sant.isObjeto() && !$e2.sr.getTipo().getNombre().equals(sant.getTipo().getNombre() ) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 33 (").append($RELOP.getLine()).append(",").append($RELOP.getCharPositionInLine()+1);
				sb.append("): tipo incompatible en operador relacional '").append($RELOP.text).append("'");
				LanzarError( sb.toString() );
			}
			if ( $e2.sr.isObjeto() && !sant.getTipo().getNombre().equals($e2.sr.getTipo().getNombre() ) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 33 (").append($RELOP.getLine()).append(",").append($RELOP.getCharPositionInLine()+1);
				sb.append("): tipo incompatible en operador relacional '").append($RELOP.text).append("'");
				LanzarError( sb.toString() );
			}
			if (  sant != null && $e2.sr != null && $e2.sr.isReal())
			{
				salida.append("\tconv.r8\n");
			}
			salida.append($e2.trad);
			if ( sant != null && $e2.sr != null && sant.isReal())
			{
				salida.append("\tconv.r8\n");
			}
			salida.append(traductor.get($RELOP.text)).append("\n");
			sant = $sr;
		}
	)*
	{
		if ( $cima.equals("") )
		{
			$cima = $e1.cima;
		}
		$trad = salida.toString();
	};

esum[Simbolo sh] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = ""; $sr = null;} :
	t1=term[sh]
	{
		salida.append($t1.trad);
		$cima = $t1.cima;
		$sr = $t1.sr;
	}
	(
		ADDOP t2=term[$sr]
		{
			if (  $t1.sr != null && $t2.sr != null && ( $t2.sr.isBooleano() || $t1.sr.isBooleano() ) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 3 (").append($ADDOP.getLine()).append(",").append($ADDOP.getCharPositionInLine()+1).append("):");
				sb.append(" tipo incompatible en operador aritmetico '");
				sb.append($ADDOP.text).append("'");
				LanzarError( sb.toString() );
			}
			$cima = "int32";
			if (  $t1.sr != null && $t2.sr != null && $t2.sr.isReal())
			{
				salida.append("\tconv.r8\n");
				$sr = $t2.sr;
			}
			salida.append($t2.trad);
			if ( $t1.sr != null && $t2.sr != null && $t1.sr.isReal())
			{
				salida.append("\tconv.r8\n");
			}
			salida.append(traductor.get($ADDOP.text)).append("\n");
		}
	)*
	{
		if ( $cima.equals("") )
		{
			$cima = $t1.cima;
		}
		$trad = salida.toString();
	};

term[Simbolo sh] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = "";} :
	f1=factor[sh, ""]
	{
		salida.append($f1.trad);
		$cima = $f1.cima;
		$sr = $f1.sr;
	}
	(
		MULOP f2=factor[sh, "mul"]
		{
			if (  $f1.sr != null && $f2.sr != null && ( $f2.sr.isBooleano() || $f1.sr.isBooleano() ) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 3 (").append($MULOP.getLine()).append(",").append($MULOP.getCharPositionInLine()+1).append("):");
				sb.append(" tipo incompatible en operador aritmetico '");
				sb.append($MULOP.text).append("'");
				LanzarError( sb.toString() );
			}
			$cima = "int32";
			if (  $f1.sr != null && $f2.sr != null && $f2.sr.isReal())
			{
				salida.append("\tconv.r8\n");
				$sr = $f2.sr;
			}
			salida.append($f2.trad);
			if ( $f1.sr != null && $f2.sr != null && $f1.sr.isReal())
			{
				salida.append("\tconv.r8\n");
			}
			salida.append(traductor.get($MULOP.text)).append("\n");
		}
	)*
	{
		if ( $cima.equals("") )
		{
			$cima = $f1.cima;
		}
		$trad = salida.toString();
	};

factor[Simbolo sh, String op] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = "";} :
	base[sh, op]
	{
		$trad = $base.trad;
		$cima = $base.cima;
		$sr = $base.sr;
	}
	|
		NOT f1=factor[sh, op]
	{
		if (  $f1.sr != null && !$f1.sr.isBooleano() )
		{
			StringBuilder sb = new StringBuilder();
			sb.append("Error 4 (").append($NOT.getLine()).append(",").append($NOT.getCharPositionInLine()+1).append("):");
			sb.append(" tipo incompatible en operador logico '");
			sb.append($NOT.text).append("'");
			LanzarError( sb.toString() );
		}
		salida.append($f1.trad);
		salida.append("\tldc.i4.1\n");
		salida.append("\txor\n");
		$trad = salida.toString();
		$cima = $f1.cima;
		$sr = f1.sr;
	}
	|
		PARI ADDOP f2=factor[sh, op] PARD
		{
			salida.append($f2.trad);
			if ( $ADDOP.text.equals("-"))
			{
				salida.append("\tneg\n");
			}
			$trad = salida.toString();
			$cima = $f2.cima;
			$sr = f2.sr;
		}
	;

base[Simbolo sh, String op] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = ""; $sr = null;} :
	(
			ENTERO
			{
				if ( sh != null && sh.isReal() )
				{
					salida.append("\tldc.r8 ").append($ENTERO.text).append("\n");
				}
				else
				{
					salida.append("\tldc.i4 ").append($ENTERO.text).append("\n");
				}
				$sr = new SimboloEntero("simbolo");
				$trad = salida.toString();
				$cima = "int32";
			}
		|
			REAL
			{
				salida.append("\tldc.r8 ").append($REAL.text).append("\n");
				$trad = salida.toString();
				$sr = new SimboloReal("simbolo");
				$cima = "float64";
			}
		|
			BOOLEANO
			{
				salida.append("\tldc.i4 ");
				if ( $BOOLEANO.text.equals("True"))
					salida.append("1\n");
				else
					salida.append("0\n");
				$trad = salida.toString();
				$sr = new SimboloBooleano("simbolo");
				$cima = "bool";
			}
	)
	|
		PARI expr[sh] PARD
		{
			$trad = $expr.trad;
			$cima = $expr.cima;
			$sr = $expr.sr;
		}
	| 
		ref[op]
		{
			if ( propertyActual == null || !$ref.s.getNombre().equals("valueAux") )
			{
				if ( !$ref.s.getTipo().isArray()  && !$ref.s.isProperty() )
				{
					if ( $ref.s.isCampo() && !$ref.s.isConstante() )
						salida.append("\n\tldarg 0\n");
					salida.append("\tld").append($ref.trad);
				}
				else
				{
					salida.append($ref.trad);
					if ( !$ref.s.isProperty() )
					{
						if ( $ref.s.isReal() )
						{
							salida.append("\tldelem.r8\n");
						}
						else
						{
							salida.append("\tldelem.i4\n");
						}
					}
				}
			}
			else if ( $ref.s.isProperty() && $ref.s.getNombre().equals("valueAux") )
			{
				salida.append($ref.trad);
			}
			$trad = salida.toString();
			$cima = $ref.cima;
			$sr = $ref.s;
		}
	|
		subref[1] PARI params[$PARI] PARD
		{
			salida.append($subref.trad);
			String name = $subref.text + "[" + $params.size + "]";
			Simbolo sp = $subref.sInicio;
			TablaSimbolos ts = null;
			if ( sp.getTipo() == null || sp.getTipo().getNombre().equals(tablaSimbolos.getCurrentClass().getNombre()) )
			{
				ts = tablaSimbolos;
			}
			else
			{
				ts = ((TipoClase)sp.getTipo()).getTablaSimbolos();
			}
			if ( ts.getSimbolo(name) == null )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 25 (").append($subref.id.getLine()).append(",").append($subref.id.getCharPositionInLine()+1).append("): el metodo '");
				sb.append(tablaSimbolos.getCurrentClass().getNombre()).append(".").append($subref.text).append("(");
				sb.append($params.tError).append(")' no esta definido");
				LanzarError( sb.toString() );
			}
			if ( funcionActual == null )
				salida.append("\n\tldloc ").append($subref.sInicio.getLocalPos()).append("\n");
			else
				salida.append("\n\tldarg 0\n");
			SimboloFuncion sf = (SimboloFuncion)ts.getSimbolo(name);
			if ( sf.isConstructor() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 34 (").append($subref.id.getLine()).append(",").append($subref.id.getCharPositionInLine()+1);
				sb.append("): constructor usado incorrectamente");
				LanzarError( sb.toString() );
			}
			$cima = sf.toWrite();
			salida.append($params.trad);
			salida.append("\tcall instance ").append($cima).append(" '").append(sf.getClasePadre()).append("'::'").append(sf.getNombre()).append("'(").append(sf.getArgsAsString()).append(")\n");
			$trad = salida.toString();
			$sr = sf;
		};

ref[String op] returns [ String trad, String cima, Simbolo s, int fila, int columna, Simbolo sInicio, int cont, Token id ]
@init {StringBuilder salida = new StringBuilder(); $trad = ""; $cima = "";} :
	subref[-1]
	{
		$s = $subref.simbolo;
		$id = $subref.id;
		$cont = $subref.cont;
		$sInicio = $subref.sInicio;
		$cima = $subref.cima;
		Token id = $subref.id;
		if ( $s == null )
		{
			StringBuilder sb = new StringBuilder();
			sb.append("Error 2 (").append(id.getLine()).append(",").append(id.getCharPositionInLine()+1).append("): simbolo '");
			sb.append($subref.text).append("' no ha sido declarado");
			LanzarError( sb.toString() );
		}
		if ( $s.isProperty()  && !$s.getNombre().equals("valueAux") )
		{
			if ( op.equals("cambio"))
			{
				salida.append("\n\tcall instance void '").append(((SimboloProperty)$s).getClasePadre()).append("'::'set_").append($s.getNombre()).append("'(").append($s.toWrite()).append(")\n");
			}
			else
			{
				if ( funcionActual == null )
					salida.append("\tldloc ").append($subref.sInicio.getLocalPos()).append("\n");
				else
					salida.append("\tldarg 0\n");
				salida.append("\tcall instance ").append($s.toWrite()).append(" '").append(((SimboloProperty)$s).getClasePadre()).append("'::'get_").append($s.getNombre()).append("'()\n");				
			}
		}
		else if ( propertyActual == null && !$s.getTipo().isArray() )
		{
			if ( funcionActual != null )
			{
				salida.append($subref.trad).append("\n");
			//	salida.append($subref.trad);
				$cima = $s.toWrite();
			}
			else if ( !$s.getTipo().isArray() )
			{
				//salida.append("loc ").append( $subref.sInicio.getLocalPos() ).append("\n");
				salida.append($subref.trad);
				$cima = $s.toWrite();
			}
			else
			{
				$cima = $s.getTipo().getTipoBase().toWrite();
			}
		}
		else
		{
			if ( $s.isCampo() && !$s.getTipo().isArray() )
			{
				if ( $s.isConstante() )
					salida.append("s");
				salida.append("fld ").append($s.getTipo().toWrite()).append(" '").append(((SimboloCampo)$s).getClasePadre()).append("'::'").append($subref.text).append("'\n");
			}
			else if ( $s.getNombre().equals("valueAux") )
			{
				salida.append("\tldarg 1\n");
			}
		}
	}
	(
		CORI
		{
			if ( !$s.getTipo().isArray() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 11 (").append($subref.id.getLine()).append(",").append($subref.id.getCharPositionInLine()+1);
				sb.append("): el identificador '").append($subref.text).append("' no es de tipo array");
				LanzarError( sb.toString() );
			}
		}
		indices[$s, op] CORD
		{
			TipoArray tArr = (TipoArray)$s.getTipo();
			if ( $indices.indice < ( tArr.getDimensiones() + 1 ))
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 9 (").append($subref.id.getLine()).append(",").append($subref.id.getCharPositionInLine()+1);
				sb.append("): numero insuficiente de indices en el array '");
				sb.append($subref.text);
				sb.append("'");
				LanzarError( sb.toString() );
			}
			if ( $s.isCampo() )
			{
				if ( funcionActual != null )
				{
					salida.append("\tldarg 0\n\tldfld ").append($s.toWrite()).append(" '").append(((SimboloCampo)$s).getClasePadre()).append("'::'");
					salida.append($s.getNombre()).append("'\n");
				}
				else
				{
					salida.append("\tldloc ").append($subref.sInicio.getLocalPos()).append("\n");
					salida.append("\tld");
					if ( $s.isConstante() )
						salida.append("s");
					salida.append("fld ").append($s.toWrite()).append(" '").append(((SimboloCampo)$s).getClasePadre()).append("'::'");
					salida.append($s.getNombre()).append("'\n");
				}
			}
			else
				salida.append("\tldloc ").append($s.getLocalPos()).append("\n");
			salida.append( $indices.trad );	
			if ( !$s.getTipo().isArray() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 11 (").append($subref.id.getLine()).append(",").append($subref.id.getCharPositionInLine()+1);
				sb.append("): el identificador '");
				sb.append($subref.text);
				sb.append("' no es de tipo array");
				LanzarError( sb.toString() );
			}
			if ( tArr.getTipoSimple().isReal() )
				$cima = "float64";
			else if (tArr.getTipoSimple().isEntero() )
				$cima = "int32";
			else
				$cima = "bool";
				
		}
	)?
	{
		$trad = salida.toString();
	};

indices[Simbolo s, String op] returns[String trad, int indice]
@init{StringBuilder salida = new StringBuilder(); $indice = 1; $trad = "";}:
	e1=expr[new SimboloEntero("")]
	{
		TipoArray t = (TipoArray)s.getTipo();
		if ( t != null && t.isInicializado() )
		{
			salida.append("//Inicio acceso a vector\n\tldc.i4.0 \n");
			salida.append($e1.trad);
			salida.append("\tconv.i\n");
			if ( t != null )
				salida.append("\tldc.i4 ").append(t.getParcialSize($indice)).append("\n");
			else
				salida.append("\tldc.i4 0\n");
			salida.append("\tmul\n");
			salida.append("\tadd\n");
		}
		$indice++;
	}
	(
		COMA e2=expr[s]
		{
			if ( t != null && t.isInicializado() && !(op.equals("mul") &&  s.isBooleano() ) )
			{
				if ( $e2.sr.isBooleano() )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 13 (").append($COMA.getLine()).append(",").append($COMA.getCharPositionInLine()+1);
					sb.append("): indice de tipo incompatible");
					LanzarError( sb.toString() );
				}
				if ( t != null && $indice < t.getDimensiones() )
				{
					salida.append($e2.trad);
					salida.append("\tconv.i\n");
					salida.append("\tldc.i4 ").append(t.getParcialSize($indice)).append("\n");
					salida.append("\tmul\n");
					salida.append("\tadd\n");
				}
				else if ( t != null && $indice == t.getDimensiones() )
				{
					salida.append($e2.trad);
					salida.append("\tconv.i\n");
					salida.append("\tadd\n");
				}
				else
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 12 (").append($COMA.getLine()).append(",").append($COMA.getCharPositionInLine()+1);
					sb.append("): demasiados indices");
					LanzarError( sb.toString() );
				}
			}
			$indice++;
		}
	)*
	{
		salida.append("//FIN ACCESO VECTOR\n");
		$trad = salida.toString();
	};

miembro returns[String trad]
@init{StringBuilder salida = new StringBuilder(); $trad = "";}:
		campo
		{
			$trad = $campo.trad;
		}
	|
		metodo
		{
			$trad = $metodo.trad;
		}
	|
		propiedad
		{
			$trad = $propiedad.trad;
		};

campo returns[String trad]
@init{StringBuilder salida = new StringBuilder(); $trad = "";}:
	visibilidad
	{
		isCampo = true;
	}
	decl[$visibilidad.visibilidad]
	{
		isCampo = false;
		$trad = $decl.fTrad;
	};

visibilidad returns[boolean visibilidad, String trad]
@init{$visibilidad = false;}:
		PRIVATE
		{
			$visibilidad = false;
			$trad = $PRIVATE.text;
		}
	|
		PUBLIC
		{
			$visibilidad = true;
			$trad = $PUBLIC.text;
		};

args returns[String trad, HashMap<String, Integer> argumentos, String tError]
@init{StringBuilder salida = new StringBuilder(); StringBuilder sError = new StringBuilder(); $trad = ""; $argumentos = new HashMap<String, Integer>(); int pos = 1;}:
	(
		DOUBLE id1=ID
		{
			salida.append("float64");
			sError.append("double");
			$argumentos.put($id1.text,pos);
			pos++;
		}
		(
			COMA DOUBLE id2=ID
			{
				salida.append(", float64");
				sError.append(",double");
				$argumentos.put($id2.text,pos);
				pos++;
			}
		)*
	)?
	{
		$trad = salida.toString();
		$tError = sError.toString();
	};

propiedad returns[String trad]
@init{$trad = "";}:
	PROPERTY
	tipoSimple
	ID LLAVEI
	{
		propertyActual = new SimboloProperty("propiedadActual", $tipoSimple.tipo);
	}
	accesores[$tipoSimple.tipo, $ID.text, $ID]
	{
		propertyActual = null;
	}
	LLAVED
	{
		$trad = $accesores.trad;
	};

accesores[Tipo tipo, String id, Token tok] returns[String trad]
@init{StringBuilder salida = new StringBuilder(); $trad = "";}:
		{
			SimboloProperty s = new SimboloProperty(id, tipo);
			s.setClasePadre(tablaSimbolos.getCurrentClass().getNombre());
		}
		(
			{
				s.setGet(true);
			}
			GET b1=bloque[true,false]
			{
				String cln = tablaSimbolos.getCurrentClass().getNombre();
				salida.append(".method public ").append(tipo.toWrite()).append(" 'get_").append(id).append("'()\n{\n");
				salida.append("\t.maxstack 1000\n");
				salida.append($b1.trad);
				salida.append("\n\tldc.r8 0\n\tret\n}\n");
			}
		)?
		(
			{
				s.setSet(true);
			}
			SET b2=bloque[true,false]
			{
				salida.append(".method public void 'set_").append(id).append("'(");
				salida.append(tipo.toWrite()).append(")\n{\n");
				salida.append("\t.maxstack 1000\n");
				salida.append($b2.trad);
				salida.append("\n\tret\n}\n");
			}
		)?
		{
			if ( !s.hasGet() && !s.hasSet() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 20 (").append(tok.getLine()).append(",").append(tok.getCharPositionInLine()+1).append("): La propiedad '");
				sb.append(id).append("' debe definir al menos un accesor");
				LanzarError( sb.toString() );
			}
			tablaSimbolos.addSimbolo(s);
			salida.append(".property ").append(tipo.toWrite()).append(" ");
			salida.append(id).append("(").append(tipo.toWrite()).append(")\n{\n");
			if ( s.hasGet() )
				salida.append("\t.get instance ").append(tipo.toWrite()).append(" '").append(tablaSimbolos.getCurrentClass().getNombre()).append("'::'get_").append(id).append("'()\n");
			if ( s.hasSet() )
				salida.append("\t.set instance void '").append(tablaSimbolos.getCurrentClass().getNombre()).append("'::'set_").append(id).append("'(").append(tipo.toWrite()).append(")\n");
			salida.append("}\n");
			$trad = salida.toString();
		}
	|
		GET PYC SET PYC
		{
			String cln = tablaSimbolos.getCurrentClass().getNombre();
			salida.append(".field private ").append(tipo.toWrite()).append(" '_value_").append(id).append("'\n");
			salida.append(".method public ").append(tipo.toWrite()).append(" 'get_").append(id).append("'()\n{\n");
			salida.append("\t.maxstack 2\n\tldarg 0\n");
			salida.append("\tldfld ").append(tipo.toWrite()).append(" '").append(cln).append("'::'_value_").append(id);
			salida.append("'\n\tret\n}\n");

			salida.append(".method public void 'set_").append(id).append("'(");
			salida.append(tipo.toWrite()).append(")\n{\n");
			salida.append("\t.maxstack 2\n\tldarg 0\n\tldarg 1\n");
			salida.append("\tstfld ").append(tipo.toWrite()).append(" '").append(cln).append("'::'_value_").append(id);
			salida.append("'\n\tret\n}\n");

			salida.append(".property ").append(tipo.toWrite()).append(" ");
			salida.append(id).append("(").append(tipo.toWrite()).append(")\n{\n");
			salida.append("\t.get instance ").append(tipo.toWrite()).append(" '").append(cln).append("'::get_").append(id).append("()\n");
			salida.append("\t.set instance void '").append(cln).append("'::'set_").append(id).append("'(").append(tipo.toWrite()).append(")\n}\n");
			$trad = salida.toString();

			SimboloProperty s = new SimboloProperty(id, tipo);
			s.setGet(true);
			s.setSet(true);
			tablaSimbolos.addSimbolo(s);
		};

params[Token tok] returns[String trad, int size, String tError, String tParams]
@init{StringBuilder salida = new StringBuilder(); StringBuilder stParams = new StringBuilder(); StringBuilder sError = new StringBuilder(); $trad = ""; $size = 0; $tError = ""; $tParams = "";}:
	(
		e1=expr[null]
		{
			$size++;
			salida.append($e1.trad);
			if ( !$e1.trad.equals("") )
				salida.append("\tconv.r8\n");
			sError.append("double");
			stParams.append("float64");
			if ( $e1.sr.isBooleano() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 28 (").append(tok.getLine()).append(",").append(tok.getCharPositionInLine()+1).append("): tipo incompatible en el parametro");
				LanzarError( sb.toString() );
			}
		}
		(
			COMA e2=expr[null]
			{
				$size++;
				salida.append($e2.trad);
				if ( !$e2.trad.equals("") )
					salida.append("\tconv.r8\n");
				sError.append(",double");
				stParams.append(",float64");
				if ( $e2.sr.isBooleano() )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 28 (").append($COMA.getLine()).append(",").append($COMA.getCharPositionInLine()+1).append("): tipo incompatible en el parametro");
					LanzarError( sb.toString() );
				}
			}
		)*
	)?
	{
		$trad = salida.toString();
		$tError = sError.toString();
		$tParams = stParams.toString();
	};


subref[int padre] returns[String trad, Token id, Simbolo simbolo, String text, Simbolo sInicio, String cima, String cln, int cont]
@init{StringBuilder salida = new StringBuilder(); $trad = ""; Simbolo ultimo = null; $sInicio = null; $cima = ""; $cln = ""; $cont = 0;}:
	id1=ID
	{
		$cont++;
		Simbolo s = tablaSimbolos.getSimbolo($id1.text);
		$sInicio = tablaSimbolos.getSimbolo($id1.text);
		if ( $id1.text.equals("value") && propertyActual != null )
		{
			s = new SimboloProperty("valueAux", propertyActual.getTipo() );
		}
		else if ( s != null && isMain && ( s.isCampo() || s.isProperty() || s.isFuncion() ) )
		{
			StringBuilder sb = new StringBuilder();
			sb.append("Error 31 (").append($id1.getLine()).append(",").append($id1.getCharPositionInLine()+1).append("): miembro '");
			sb.append($id1.text).append("' no accesible desde Main");
			LanzarError( sb.toString() );
		}
		else if ( s == null )
		{
			if ( padre == -1 || !tablaSimbolos.findMetodo($id1.text))
			{
				if ( tablaSimbolos.findMetodo($id1.text) )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 23 (").append($id1.getLine()).append(",").append($id1.getCharPositionInLine()+1).append("): identificador '");
					sb.append($id1.text).append("' usado incorrectamente");
					LanzarError( sb.toString() );
				}
				if ( funcionActual == null || !funcionActual.hasArg($id1.text))
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 2 (").append($id1.getLine()).append(",").append($id1.getCharPositionInLine()+1).append("): simbolo '");
					sb.append($id1.text).append("' no ha sido declarado");
					LanzarError( sb.toString() );
				}
				else
				{
					s = new SimboloFuncion("sAux", new TipoReal());
					$sInicio = s;
					salida.append("arg ").append(funcionActual.getArgs().get($id1.text)).append("\n");
				}
			}
			else
			{
				s = new Simbolo("metodoAux", null);
				$sInicio = s;
			}
		}
		else
		{
			if ( s.isCampo() )
			{
				if ( s.isConstante() )
					salida.append("s");
				salida.append("fld ").append(s.toWrite()).append(" '").append(((SimboloCampo)s).getClasePadre()).append("'::'");
				salida.append(s.getNombre()).append("'\n");
			}
			else if ( !s.isProperty() )
			{
				salida.append("loc ").append(s.getLocalPos()).append("\n");
			}
			else
			{
				salida.append("arg 0\n");
			}
		};
		ultimo = s;
		$simbolo = s;
		$id = $id1;
		$text = $id1.text;
	}
	(
		PUNTO id2=ID
		{
			$cont++;
			salida = new StringBuilder();
			if ( ultimo == null || !( ultimo.isObjeto() || ( ultimo.isCampo() && ultimo.getTipo().isClass() ) ) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 23 (").append($id1.getLine()).append(",").append($id1.getCharPositionInLine()+1).append("): identificador '");
				sb.append($id1.text).append("' usado incorrectamente");
				LanzarError( sb.toString() );
			}
			if ( isMain && ultimo.isCampo() || ultimo.isProperty() || ultimo.isFuncion() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 31(").append($id1.getLine()).append(",").append($id1.getCharPositionInLine()+1).append("): miembro '");
				sb.append($id1.text).append("' no accesible desde Main");
				LanzarError( sb.toString() );
			}
			TablaSimbolos ts = null;
			boolean mismaClase = true;
			if ( ultimo.getTipo() == null || ultimo.getTipo().getNombre().equals(tablaSimbolos.getCurrentClass().getNombre()) )
			{
				ts = tablaSimbolos;
			}
			else
			{
				mismaClase = false;
				ts = ((TipoClase)ultimo.getTipo()).getTablaSimbolos();
			}
			$cln = ts.getCurrentClass().getNombre();
			s = ts.getSimbolo($id2.text);
			if ( s != null && !mismaClase && !s.isVisible() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 2 (").append($id2.getLine()).append(",").append($id2.getCharPositionInLine()+1).append("): simbolo '");
				sb.append($id2.text).append("' no ha sido declarado");
				LanzarError( sb.toString() );
			}
			if ( s == null )
			{
				if ( padre == -1 || !ts.findMetodo($id2.text))
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 2 (").append($id2.getLine()).append(",").append($id2.getCharPositionInLine()+1).append("): simbolo '");
					sb.append($id2.text).append("' no ha sido declarado");
					LanzarError( sb.toString() );
				}
			}
			if ( padre == -1 )
			{
				if ( !s.isProperty() )
					salida.append("\tldfld ").append(s.getTipo().toWrite()).append(" '").append($cln).append("'::'").append($id2.text).append("'\n");
				else
				{
					$cima = s.getTipo().toWrite();
					salida = new StringBuilder();
//					salida.append("\tldloc ").append(ultimo.getLocalPos()).append("\n");
//					salida.append("\tcall instance ").append(s.toWrite()).append(" '").append(cln).append("'::'").append($id2.text).append("'()\n");
				}
			}
			ultimo = s;
			$simbolo = ultimo;
			$id = $id2;
			$text = $id2.text;
		}
	)*
	{
		$trad = salida.toString();
	};

/* Analizador léxico: */ 
CLASS		:	'class';
//SINGLE		:	'Single';
VOID		:	'void';
MAIN		:	'Main';
INT			:	'int';
DOUBLE		:	'double';
BOOL		:	'bool';
PUBLIC		:	'public';
STATIC		:	'static';
IF			:	'if';
ELSE		:	'else';
FOR			:	'for';
TO			:	'to';
STEP		:	'step';
WHILE		:	'while';
BREAK		:	'break';
CONTINUE	:	'continue';
NEW			:	'new';
WRITELINE	:	'System.Console.WriteLine';
READLINE	:	('int'|'double'|'bool')'.Parse(System.Console.ReadLine())';
LLAVEI		:	'{';
LLAVED		:	'}';
PARI		:	'(';
PARD		:	')';
CORI		:	'[';
CORD		:	']';
COMA		:	',';
PYC			:	';';
ASIG		:	'=';
OR			:	'|';
AND			:	'&';
RELOP		:	'=='|'!='|'<' | '>' | '<=' | '>=';
ADDOP		:	'+' | '-';
MULOP		:	'*' | '/';
NOT			:	'!';
PUNTO		:	'.';
ENTERO		:	('0'..'9')+;
REAL		:	('0'..'9')+'.'('0'..'9')+;
BOOLEANO	:	'True' | 'False';
CONST		:	'const';
EXPLAIN		:	'explain';
TRY			:	'try';
CATCH		:	'catch';
THROW		:	'throw';
EXCEPTION	:	'Exception';
RETURN		:	'return';
PRIVATE		:	'private';
GET			:	'get';
SET			:	'set';
PROPERTY	:	'property';
ID			:	('a'..'z'|'A'..'Z'|'_')('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
NEWLINE		:	('\r'? '\n'|' '|'\t')+  {skip();}; 
COMENTARIO	:	'/*' .* '*/' {skip();};
COMENLINEA	:	'//' .* '\n'  {skip();};
